﻿namespace FlexField.Metadata
{
    /// <summary>
    /// Provides the default validation of types.
    /// </summary>
    public class EntityValidator<TKey> : IEntityValidator<TKey> where TKey : IEquatable<TKey>
    {
        private readonly MetadataErrorDescriber _errors;

        /// <summary>
        /// Creates a new instance of <see cref="EntityValidator{TKey}"/>.
        /// </summary>
        /// <param name="errors">The <see cref="MetadataErrorDescriber"/> used to provider error messages.</param>
        public EntityValidator(MetadataErrorDescriber errors = null)
        {
            _errors = errors ?? new MetadataErrorDescriber();
        }

        /// <inheritdoc />
        public async Task<MetadataResult> ValidateAsync(MetadataManager<TKey> manager, MetadataEntity<TKey> entity)
        {
            if (manager == null)
            {
                throw new ArgumentNullException(nameof(manager));
            }

            if (entity == null)
            {
                throw new ArgumentNullException(nameof(entity));
            }

            var errors = new List<MetadataError>();
            await ValidateTypeName(manager, entity, errors);
            if (errors.Count > 0)
            {
                return MetadataResult.Failed(errors.ToArray());
            }
            return MetadataResult.Success;
        }

        private async Task ValidateTypeName(MetadataManager<TKey> manager, MetadataEntity<TKey> entity, ICollection<MetadataError> errors)
        {
            if (string.IsNullOrWhiteSpace(entity.EntityName))
            {
                errors.Add(_errors.InvalidTypeName(entity.EntityName));
            }
            else if (!string.IsNullOrEmpty(manager.Options.AllowedNameCharacters) && entity.EntityName.Any(c => !manager.Options.AllowedNameCharacters.Contains(c)))
            {
                errors.Add(_errors.InvalidTypeName(entity.EntityName));
            }
            else
            {
                var owner = await manager.FindTypeByNameAsync(entity.EntityName);
                if (owner !=null && !Equals(owner.Id, entity.Id))
                {
                    errors.Add(_errors.DuplicateTypeName(entity.EntityName));
                }
            }
        }
    }
}
